import numpy as np
import matplotlib.pyplot as plt 

DNA_SIZE = 10
POP_SIZE = 100  #种群人数
CROSS_RATE = 0.8  #80%交叉配对
MUTATION_RATE = 0.003  #变异概率
N_GENERATIONS = 200  #迭代次数
X_BOUND = [0,5]

def F(x):
    return np.sin(10*x)*x + np.cos(2*x)*x

def get_fitness(pred):
    #防止等于0
    return pred + 1e-3 - np.min(pred)

#二进制转十进制
def translateDNA(pop):
    return pop.dot(2**np.arange(DNA_SIZE)[::-1])/(2**DNA_SIZE-1)*X_BOUND[1]

#适者生存
def select (pop,fitness):
    idx = np.random.choice(np.arange(POP_SIZE),size=POP_SIZE,replace=True,p=fitness/fitness.sum())
    return pop[idx]

#交叉配对
def crossover(parent,pop):
    if np.random.rand() < CROSS_RATE:
        i_ = np.random.randint(0,POP_SIZE,size=1)
        cross_point = np.random.randint(0,2,size=DNA_SIZE).astype(np.bool)
        #相当于基因结合
        parent[cross_point] = pop[i_,cross_point]
    return parent

#变异
def mutate(child):
    for point in range(DNA_SIZE):
        if np.random.rand() < MUTATION_RATE:
            child[point] = 1 if child[point] == 0 else 0
    return child

pop = np.random.randint(0,2,(1,DNA_SIZE)).repeat(POP_SIZE,axis=0)

for _ in range(N_GENERATIONS):
    F_VALUES = F(translateDNA(pop))

    fitness = get_fitness(F_VALUES)
    pop = select(pop,fitness)
    pop_copy = pop.copy()
    for parent in pop:
        child = crossover(parent,pop_copy)
        child = mutate(child)
        parent[:] = child
plt.ioff();plt.show()

